package grapho;

import edu.uci.ics.jung.algorithms.layout.FRLayout;
import edu.uci.ics.jung.algorithms.layout.Layout;
import edu.uci.ics.jung.algorithms.shortestpath.DijkstraShortestPath;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.RenderContext;
import edu.uci.ics.jung.visualization.VisualizationViewer;
import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse;
import edu.uci.ics.jung.visualization.control.ModalGraphMouse;
import edu.uci.ics.jung.visualization.decorators.ToStringLabeller;
import edu.uci.ics.jung.visualization.renderers.Renderer;
import edu.uci.ics.jung.visualization.transform.shape.GraphicsDecorator;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Paint;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.collections15.Transformer;

public class Estrutura
{
    public static final Paint DEFAULT_ORIGEM_COLOR = Color.BLUE;
    public static final Paint DEFAULT_DESTINO_COLOR = Color.RED;
    public static final Paint DEFAULT_PATH_COLOR_LEVEL_1 = Color.GREEN;
    public static final Paint DEFAULT_PATH_COLOR_LEVEL_2 = Color.ORANGE;
    public static final Paint DEFAULT_PATH_COLOR_LEVEL_3 = Color.YELLOW;
    public static final Paint DEFAULT_PATH_COLOR_LEVEL_4 = Color.PINK;
    public static final Paint DEFAULT_PATH_COLOR_LEVEL_5 = Color.CYAN;
    public static final Paint[] DEFAULT_PATH_LEVEL_COLORS = {DEFAULT_PATH_COLOR_LEVEL_1, DEFAULT_PATH_COLOR_LEVEL_2, DEFAULT_PATH_COLOR_LEVEL_3, DEFAULT_PATH_COLOR_LEVEL_4, DEFAULT_PATH_COLOR_LEVEL_5};
    
    private Montagem montagem;
    private Layout<Vertice, Aresta> layout;
    private VisualizationViewer<Vertice, Aresta> visualiza;
    private Graph dataManager;
    
    public Estrutura(Dimension dimension)
    {
        dataManager = new Graph();
        
        montagem = new Montagem();
        
        layout = new FRLayout<Vertice, Aresta>(montagem);
        layout.setSize(dimension);
        
        DefaultModalGraphMouse defaultModalGraphMouse = new DefaultModalGraphMouse(1.0f, 1.0f);
        defaultModalGraphMouse.setMode(ModalGraphMouse.Mode.PICKING);
        
        visualiza = new VisualizationViewer<Vertice, Aresta>(layout);
        visualiza.setGraphMouse(defaultModalGraphMouse);
        visualiza.setSize(dimension);
        visualiza.getRenderContext().setVertexLabelTransformer(new ToStringLabeller());
        visualiza.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR);
        
        visualiza.getRenderer().setVertexRenderer(new Renderer.Vertex<Vertice, Aresta>()
        {
            @Override
            public void paintVertex(RenderContext<Vertice, Aresta> rc, Layout<Vertice, Aresta> layout, Vertice v)
            {
                GraphicsDecorator graphicsContext = rc.getGraphicsContext();
                Point2D center = layout.transform(v);
                
                Shape shape = new Rectangle2D.Double(center.getX() - 25, center.getY() - 8, 50, 15);
                
                graphicsContext.setPaint(v.getColor());
                graphicsContext.fill(shape);
            }
        });
        
        visualiza.getRenderContext().setEdgeDrawPaintTransformer(new Transformer<Aresta, Paint>()
        {
            @Override
            public Paint transform(Aresta graphEdge)
            {
                return graphEdge.getColor();
            }
        });
    }
    
    public Montagem getMontagem()
    {
        return montagem;
    }
    
    public Layout<Vertice, Aresta> getLayout()
    {
        return layout;
    }
    
    public BasicVisualizationServer<Vertice, Aresta> getVisualiza()
    {
        return visualiza;
    }
    
    public void removeFilter()
    {
        for(Aresta aresta : montagem.getEdges())
        {
            aresta.setColor(Aresta.DEFAULT_COLOR);
            
            visualiza.getRenderContext().getEdgeDrawPaintTransformer().transform(aresta);
        }
        
        for(Vertice vertex : montagem.getVertices())
        {
            vertex.setColor(Vertice.DEFAULT_COLOR);
            
            visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(vertex);
        }
        
        visualiza.repaint();
    }
    
    public List<Aresta> calculateShortestPath(int sourceRm, int targetRm)
    {
        removeFilter();
        
        DijkstraShortestPath dijkstra = new DijkstraShortestPath(montagem);
        List<Aresta> shortestPath = dijkstra.getPath(montagem.getGraphoVertice().get(sourceRm), montagem.getGraphoVertice().get(targetRm));
        
        for(Aresta graphEdge : shortestPath)
        {
            graphEdge.setColor(DEFAULT_ORIGEM_COLOR);
            
            visualiza.getRenderContext().getEdgeDrawPaintTransformer().transform(graphEdge);
        }
        
        Vertice sourceGraphVertex = montagem.getGraphoVertice().get(sourceRm);
        sourceGraphVertex.setColor(DEFAULT_ORIGEM_COLOR);
        
        Vertice targetGraphVertex = montagem.getGraphoVertice().get(targetRm);
        targetGraphVertex.setColor(DEFAULT_DESTINO_COLOR);
        
        visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(sourceGraphVertex);
        visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(targetGraphVertex);
        
        visualiza.repaint();
        
        return shortestPath;
    }
    
    public List<Vertice> calculateUntouchableVertexes(int sourceRm)
    {
        removeFilter();
        
        ArrayList<Vertice> untouchableVertexes = new ArrayList<Vertice>();
        
        for(Aluno student : dataManager.getEstudanteDesconectado(sourceRm))
        {
            untouchableVertexes.add(montagem.getGraphoVertice().get(student.getRm()));
        }
        
        Vertice sourceGraphVertex = montagem.getGraphoVertice().get(sourceRm);
        sourceGraphVertex.setColor(DEFAULT_ORIGEM_COLOR);        
        
        visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(sourceGraphVertex);
        
        for(Vertice graphVertex : untouchableVertexes)
        {
            graphVertex.setColor(DEFAULT_DESTINO_COLOR);
            
            visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(graphVertex);
        }
        
        visualiza.repaint();
        
        return untouchableVertexes;
    }
    
    public List<List<Vertice>> calculateTouchableVertexes(int sourceRm)
    {
        return calculateTouchableVertexes(sourceRm, -1);
    }
    
    public List<List<Vertice>> calculateTouchableVertexes(int sourceRm, int maxDeep)
    {
        removeFilter();
        
        ArrayList<List<Vertice>> touchableVertexes = new ArrayList<List<Vertice>>();
        ArrayList<Aresta> path = new ArrayList<Aresta>();
        
        for(ArrayList<Aluno> students : dataManager.getEstudanteConectado(sourceRm, maxDeep))
        {
            ArrayList<Vertice> tmpGraphVertexes = new ArrayList<Vertice>();
            
            for(Aluno student : students)
            {
                tmpGraphVertexes.add(montagem.getGraphoVertice().get(student.getRm()));
            }

            touchableVertexes.add(tmpGraphVertexes);
        }
        
        Vertice sourceGraphVertex = montagem.getGraphoVertice().get(sourceRm);
        sourceGraphVertex.setColor(DEFAULT_ORIGEM_COLOR);
        
        for(int i = 0, iLen = touchableVertexes.size(); i < iLen; i++)
        {
            for(int j = 0, jLen = touchableVertexes.get(i).size(); j < jLen; j++)
            {
                if(i == 0)
                {
                    Aresta graphEdge = getArestaNoVertice(sourceGraphVertex, touchableVertexes.get(i).get(j));
                    graphEdge.setColor(DEFAULT_PATH_LEVEL_COLORS[i]);
                    
                    path.add(graphEdge);
                }
                else
                {
                    for(int k = 0, kLen = touchableVertexes.get(i - 1).size(); k < kLen; k++)
                    {
                        Aresta graphEdge = getArestaNoVertice(touchableVertexes.get(i - 1).get(k), touchableVertexes.get(i).get(j));
                        
                        if(graphEdge != null)
                        {
                            graphEdge.setColor(DEFAULT_PATH_LEVEL_COLORS[i]);
                            
                            path.add(graphEdge);
                        }
                    }
                }
            }
        }
        
        visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(sourceGraphVertex);
        
        for(List<Vertice> graphVertexes : touchableVertexes)
        {
            for(Vertice graphVertex : graphVertexes)
            {
                graphVertex.setColor(DEFAULT_DESTINO_COLOR);

                visualiza.getRenderContext().getVertexDrawPaintTransformer().transform(graphVertex);
            }
        }
        
        for(Aresta graphEdge: path)
        {
            visualiza.getRenderContext().getEdgeDrawPaintTransformer().transform(graphEdge);
        }
        
        visualiza.repaint();
        
        return touchableVertexes;
    }
    
    public Aresta getArestaNoVertice(Vertice sourceVertex, Vertice targetVertex)
    {
        return montagem.getArestas().get(montagem.generateEdgeKey(sourceVertex.getRm(), targetVertex.getRm()));
    }
}
